package org.freespace.service.impl;

import cn.hutool.core.io.FileUtil;
import cn.hutool.crypto.digest.MD5;
import cn.hutool.json.JSONArray;
import cn.hutool.json.JSONObject;
import cn.hutool.json.JSONUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.blazemeter.jmeter.threads.arrivals.ArrivalsThreadGroup;
import com.blazemeter.jmeter.threads.concurrency.ConcurrencyThreadGroup;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.exception.ExceptionUtils;
import org.apache.jmeter.assertions.ResponseAssertion;
import org.apache.jmeter.config.Arguments;
import org.apache.jmeter.config.CSVDataSet;
import org.apache.jmeter.config.ConfigTestElement;
import org.apache.jmeter.control.LoopController;
import org.apache.jmeter.control.ThroughputController;
import org.apache.jmeter.control.TransactionController;
import org.apache.jmeter.protocol.http.control.HeaderManager;
import org.apache.jmeter.protocol.http.sampler.HTTPSamplerProxy;
import org.apache.jmeter.protocol.tcp.sampler.TCPSampler;
import org.apache.jmeter.save.SaveService;
import org.apache.jmeter.testelement.property.CollectionProperty;
import org.apache.jmeter.threads.ThreadGroup;
import org.apache.jmeter.util.JMeterUtils;
import org.apache.jorphan.collections.HashTree;
import org.freespace.Constant;
import org.freespace.config.GlobalConf;
import org.freespace.model.*;
import org.freespace.mapper.JmxScriptMapper;
import org.freespace.service.*;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import org.freespace.utils.SessionUtil;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.web.multipart.MultipartFile;

import java.io.*;
import java.nio.charset.Charset;
import java.time.LocalDateTime;
import java.util.*;

import static org.freespace.Constant.JMX_LOCAL_DIR;

/**
 * <p>
 * jmx脚本文件表 服务实现类
 * </p>
 *
 * @author liuqi
 * @since 2023-04-27
 */
@Slf4j
@Service
public class JmxScriptServiceImpl extends ServiceImpl<JmxScriptMapper, JmxScript> implements IJmxScriptService {

    @Autowired
    IJmxThreadgroupService threadgroupService;
    @Autowired
    IJmxArgumentsService argumentsService;
    @Autowired
    IJmxSampleService sampleService;
    @Autowired
    IJmxHttpRequestDefaultService httpRequestDefaultService;
    @Autowired
    IJmxParameterService parameterService;

    @Autowired
    IJmxParamfileService paramfileService;
    @Autowired
    IJmxAssertionService assertionService;
    @Autowired
    IJmxHeaderService   headerService;
    @Autowired
    IJmxUserDefineVariableService userDefineVariableService;

    @Override
    public void addOrEditJmxScript(MultipartFile file, JmxScript script) throws IOException {

        if (file == null || file.getInputStream() == null) {
//            throw
        }

        script.setCreateBy(SessionUtil.getCurrentUser());
        script.setLastUpdateBy(SessionUtil.getCurrentUser());
        script.setCreateTime(LocalDateTime.now());
        script.setLastUpdateTime(LocalDateTime.now());
        saveOrUpdate(script);
        log.info("script:{}", script);

        String storeDir = Constant.JMX_FILE_SERVER_DIR + "/" + script.getId();
        File storeDirFile = new File(storeDir);
        if (!storeDirFile.exists()) {
            FileUtil.mkdir(storeDir);
            log.info("mkdir.storeDir");
        }

        String scriptFileDir = storeDir + "/" + file.getOriginalFilename();
        File scriptFile = new File(scriptFileDir);
        if (scriptFile.exists()) {
            scriptFile.delete();
            log.info("delete old file");
        }

        script.setPath(scriptFileDir);
        updateById(script);
        log.info("update storedir to table");


        File newFile = FileUtil.touch(new File(scriptFileDir));
        FileUtil.writeFromStream(file.getInputStream(), newFile, true);
        log.info("wirite upload file to dir");

        List<JmxParamfile> paramfileList = new ArrayList<>();
        // scriptId不为空，查找对于的参数化文件关联关系
        if (script.getId() != null) {
            paramfileList = paramfileService.list(Wrappers.<JmxParamfile>lambdaQuery().eq(JmxParamfile::getScriptId, script.getId()));
        }

        //绑定参数化文件

        //绑定插件

        try {
            //解析线程组设置并保存到数据库
            initThreadGroup1(script);

            //解析参数化文件设置并保存到数据库
            initCSVData1(script);
        }catch (Exception e) {
            log.error(ExceptionUtils.getStackTrace(e));
        } finally {

        }
    }

    private void initCSVData1(JmxScript script) throws Exception {
        parameterService.deleteJmxParameterByJmxId(script.getId());
        Map<String, JmxParameter> map = new HashMap<>();

        InputStream in = FileUtil.getInputStream(script.getPath());

        String localJmxDir = JMX_LOCAL_DIR + File.separator + script.getId();
        File localJmxDirFile = new File(localJmxDir);
        if (!localJmxDirFile.exists()) {
            localJmxDirFile.mkdirs();
        }

        String localJmxFileFile = localJmxDir + script.getPath().substring(script.getPath().lastIndexOf("/"));
        File jmxFile = FileUtil.writeFromStream(in, localJmxFileFile);
        initProperties();
        HashTree jmxTree = SaveService.loadTree(jmxFile);

        for (HashTree item : jmxTree.values()) {
            initCSV1(item, map, script);
        }

        if (!map.isEmpty()) {
            for (String key : map.keySet()) {
                JmxParameter jmxParameter = map.get(key);
                jmxParameter.setCreateTime(LocalDateTime.now());
                parameterService.save(jmxParameter);
            }
        }
    }

    private void initCSV1(HashTree hashTree, Map<String, JmxParameter> map, JmxScript script) {
        for (Object item : hashTree.keySet()) {
            if (item instanceof CSVDataSet) {
                anlCsvDataset(map, script, (CSVDataSet) item);
            }
        }

        for (HashTree item : hashTree.values()) {
            Set<Object> keys = item.keySet();
            for (Object key : keys) {
                if (key instanceof CSVDataSet) {
                    anlCsvDataset(map, script, (CSVDataSet) key);
                } else {
                    initCSV1(item, map, script);
                }
            }

        }

    }

    private void anlCsvDataset(Map<String, JmxParameter> map, JmxScript script, CSVDataSet csvDataSet) {
        if (csvDataSet.isEnabled()) {
            JmxParameter jmxParameter = new JmxParameter();
            StringBuffer sb = new StringBuffer();
            sb.append(script.getId());
            String fileName = csvDataSet.getPropertyAsString("filename");
            if (StringUtils.isNotBlank(fileName)) {
                return;
            }
            jmxParameter.setFileName(fileName);
            sb.append(fileName);

            String fileEncoding = csvDataSet.getPropertyAsString("fileEncoding");
            jmxParameter.setFileEncoding(fileEncoding);
            sb.append(fileEncoding);

            String delimiter = csvDataSet.getPropertyAsString("delimiter");
            jmxParameter.setDelimiter(delimiter);
            sb.append(delimiter);

            String shareMode = csvDataSet.getPropertyAsString("shareMode");
            jmxParameter.setShareMode(shareMode);
            sb.append(shareMode);

            String variableNames = csvDataSet.getPropertyAsString("variableNames");
            if (StringUtils.isBlank(variableNames)) {
                return;
            }
            jmxParameter.setVariableNames(variableNames);
            sb.append(variableNames);

            Boolean quotedData = csvDataSet.getPropertyAsBoolean("quotedData");
            jmxParameter.setQuotedData(quotedData);
            sb.append(quotedData);

            Boolean recycle = csvDataSet.getPropertyAsBoolean("recycle");
            jmxParameter.setRecycle(recycle);
            sb.append(recycle);

            Boolean stopThread = csvDataSet.getPropertyAsBoolean("stopThread");
            jmxParameter.setStopThead(stopThread);
            sb.append(stopThread);

            Boolean ignoreFirstLine = csvDataSet.getPropertyAsBoolean("ignoreFirstLine");
            jmxParameter.setIgnoreFirstLine(ignoreFirstLine);
            sb.append(ignoreFirstLine);

            jmxParameter.setJmxId(script.getId());
            jmxParameter.setCreateBy(script.getCreateBy());
            jmxParameter.setOriginName("");
            jmxParameter.setCurrentName("");
            String u = MD5.create().digestHex(sb.toString());
            jmxParameter.setUniqueId(u);
            if (!map.containsKey(u)) {
                map.put(u, jmxParameter);
            }

        }
    }

    @Override
    public void initThreadGroup1(JmxScript script) throws Exception {
         threadgroupService.deleteJmxThreadgroupByJmxId(script.getId());
         argumentsService.deleteJmxArgumentsByJmxId(script.getId());
         sampleService.deleteJmxSampleByJmxId(script.getId());
         httpRequestDefaultService.deleteJmxHttpRequestDefaultByJmxId(script.getId());

        InputStream in = FileUtil.getInputStream(script.getPath());

        String localJmxDir = JMX_LOCAL_DIR + File.separator + script.getId();
        log.info("localJmxDir:{}", localJmxDir);
        File localJmxDirFile = new File(localJmxDir);
        log.info("localJmxDirFile.exists:{}", localJmxDirFile.exists());
        if (!localJmxDirFile.exists()) {
            localJmxDirFile.mkdirs();
        }

        String localJmxFileFile = localJmxDir + script.getPath().substring(script.getPath().lastIndexOf("/"));
        log.info("localJmxFileFile:{}", localJmxFileFile);
        File jmxFile = FileUtil.writeFromStream(in, localJmxFileFile);
        initProperties();
        HashTree jmxTree = SaveService.loadTree(jmxFile);

        Set keys = jmxTree.keySet();
        for (Object key : keys) {
            initAbstractThreadGroup(key, jmxTree, script);
        }

        for (HashTree item : jmxTree.values()) {
            Set treeKeys = item.keySet();
            for (Object key : treeKeys) {
                if (key instanceof ConfigTestElement) {
                    initHttpRequestDefault1((ConfigTestElement) key, script, null);
                }  else if (key instanceof Arguments) {
                    initArguments1((Arguments) key, script, null);
                }else if (key instanceof HeaderManager) {
                    initHeaderManager1((HeaderManager) key, script, null);
                }else if (key instanceof ResponseAssertion) {
                    initResponseAssertion1((ResponseAssertion) key, script, null);
                }
                initAbstractThreadGroup(key, item, script);
            }
        }

        SaveService.saveTree(jmxTree, new FileOutputStream(jmxFile));

        //上传到文件服务器覆盖旧的脚本文件
    }

    private void initHeaderManager1(HeaderManager key, JmxScript jmxScript, JmxThreadgroup jmxThreadgroup) {
        String name = key.getPropertyAsString(ConfigTestElement.NAME);
        JSONObject jsonObject = new JSONObject();
        if (StringUtils.isNotBlank(key.getHeaders().getStringValue())) {
            for (int i = 0; i < key.getHeaders().size(); i++) {
                String[] head = key.getHeaders().get(i).getStringValue().split("\t");
                if(head.length>1){
                    jsonObject.put(head[0], head[1]);
                }
            }
        }
        Boolean status = key.isEnabled();
        JmxHeader jmxHeader = new JmxHeader();
        jmxHeader.setJmxId(jmxScript.getId());
        if (jmxThreadgroup != null) {
            jmxHeader.setThreadgroupId(jmxThreadgroup.getId());
        }
        jmxHeader.setStatus(status);
        jmxHeader.setName(name);
        jmxHeader.setHeaders(jsonObject.toString());
        jmxHeader.setCreateTime(LocalDateTime.now());
        jmxHeader.setCreateBy(jmxScript.getCreateBy());
        jmxHeader.setLastUpdateBy(jmxScript.getCreateBy());
        jmxHeader.setLastUpdateTime(LocalDateTime.now());
        headerService.save(jmxHeader);
    }
    private void initHeaderManager2(HashTree hashTree, JmxScript script, JmxThreadgroup jmxThreadgroup) {
        for (Object key : hashTree.keySet()) {
            if (key instanceof HeaderManager) {
                initHeaderManager1((HeaderManager) key, script, jmxThreadgroup);
            }
        }
    }

    private void initArguments1(Arguments arguments, JmxScript script, JmxThreadgroup jmxThreadgroup) {
        Boolean status = arguments.isEnabled();
        String name = getName(arguments.getName(),script);
        arguments.setName(name);

//
        JSONArray jsonArray = new JSONArray();
        for (int i=0;i<arguments.getArgumentCount();i++) {
            String aKey = arguments.getArgument(i).getName();
            String aValue = arguments.getArgument(i).getValue();
            String desc = arguments.getArgument(i).getDescription();
            JSONObject subJson = new JSONObject();
            subJson.put("name", aKey);
            subJson.put("value", aValue);
            subJson.put("desc", desc);
            jsonArray.add(subJson);
            if (script.getId() != null) {
                JmxUserDefineVariable jmxUserDefineVariable = new JmxUserDefineVariable();
                jmxUserDefineVariable.setJmxId(script.getId());
                jmxUserDefineVariable.setVarName(aKey);
                jmxUserDefineVariable.setVarValue(aValue);
                String useName = "${"+aKey+"}";
                jmxUserDefineVariable.setUseName(useName);
                jmxUserDefineVariable.setCreateBy(SessionUtil.getCurrentUser());
                jmxUserDefineVariable.setCreateTime(LocalDateTime.now());
                userDefineVariableService.save(jmxUserDefineVariable);
            }
        }

        JmxArguments jmxArguments = new JmxArguments();
        jmxArguments.setStatus(status);
        jmxArguments.setName(name);
        jmxArguments.setComments(arguments.getComment());
        jmxArguments.setArguments(jsonArray.toString());
        jmxArguments.setJmxId(script.getId());
        if (jmxThreadgroup != null) {
            jmxArguments.setThreadgroupId(jmxThreadgroup.getId());
        }
        jmxArguments.setCreateTime(LocalDateTime.now());
        jmxArguments.setCreateBy(script.getCreateBy());
        jmxArguments.setLastUpdateBy(script.getCreateBy());
        jmxArguments.setLastUpdateTime(LocalDateTime.now());
        argumentsService.save(jmxArguments);

    }


    private void initArguments2(HashTree hashTree, JmxScript script, JmxThreadgroup jmxThreadgroup) {
        for (Object key : hashTree.keySet()) {
            if (key instanceof Arguments) {
                initArguments1((Arguments) key, script, jmxThreadgroup);
            }
        }
    }


    private void initHttpRequestDefault1(ConfigTestElement key, JmxScript script, JmxThreadgroup jmxThreadgroup) {
        if (key.getPropertyAsString(ConfigTestElement.GUI_CLASS).equals("org.apache.jmeter.protocol.http.config.gui.HttpDefaultsGui")) {
            Boolean status = key.isEnabled();
            String name = key.getPropertyAsString(ConfigTestElement.NAME);
            String domain = key.getPropertyAsString(HTTPSamplerProxy.DOMAIN);
            String port = key.getPropertyAsString(HTTPSamplerProxy.PORT);
            String protocol = key.getPropertyAsString(HTTPSamplerProxy.PROTOCOL);
            String path = key.getPropertyAsString(HTTPSamplerProxy.PATH);
            String contentEncoding = key.getPropertyAsString(HTTPSamplerProxy.CONTENT_ENCODING);
            String request = key.getPropertyAsString(HTTPSamplerProxy.POST);


            JmxHttpRequestDefault jmxHttpRequestDefault = new JmxHttpRequestDefault();
            jmxHttpRequestDefault.setName(name);
            jmxHttpRequestDefault.setStatus(status);
            jmxHttpRequestDefault.setIp(domain);
            jmxHttpRequestDefault.setPort(port);
            jmxHttpRequestDefault.setProtocol(protocol);
            jmxHttpRequestDefault.setPath(path);
            jmxHttpRequestDefault.setEncode(contentEncoding);
            jmxHttpRequestDefault.setRequest(request);
            jmxHttpRequestDefault.setJmxId(script.getId());
            if (jmxThreadgroup != null) {
                jmxHttpRequestDefault.setThreadgroupId(jmxThreadgroup.getId());
            }
            jmxHttpRequestDefault.setCreateTime(LocalDateTime.now());
            jmxHttpRequestDefault.setCreateBy(script.getCreateBy());
            jmxHttpRequestDefault.setLastUpdateBy(script.getCreateBy());
            jmxHttpRequestDefault.setLastUpdateTime(LocalDateTime.now());
            httpRequestDefaultService.save(jmxHttpRequestDefault);
        }
    }
    private void initHttpRequestDefault2(HashTree hashTree, JmxScript script, JmxThreadgroup jmxThreadgroup) {
        for (Object key : hashTree.keySet()) {
            if (key instanceof ConfigTestElement) {
                initHttpRequestDefault1((ConfigTestElement) key, script, jmxThreadgroup);
            }
        }
    }
    private void initAbstractThreadGroup(Object key, HashTree item, JmxScript script) {
        if (key instanceof ThreadGroup) {
            JmxThreadgroup jmxThreadgroup = new JmxThreadgroup();

            Boolean flag = ((ThreadGroup) key).getPropertyAsBoolean(ThreadGroup.ENABLED);
            if (flag) {
                jmxThreadgroup.setStatus(1);
            }else{
                jmxThreadgroup.setStatus(0);
            }

            jmxThreadgroup.setJmxId(script.getId());
            String testName = ((ThreadGroup) key).getPropertyAsString(ThreadGroup.NAME);
            testName = getTestName(testName, script);
            ((ThreadGroup) key).setProperty(ThreadGroup.NAME, testName);
            jmxThreadgroup.setTestName(testName);
            String testClass = ((ThreadGroup) key).getPropertyAsString(ThreadGroup.TEST_CLASS);
            jmxThreadgroup.setTestClazz(testClass);

            String type = "";
            if (StringUtils.isNotBlank(testClass)) {
                type = testClass.substring(testClass.lastIndexOf(".")+1, testClass.length());
            }
            jmxThreadgroup.setType(type);
            Map<String, Object> attMap = new HashMap<>();
            long duration = ((ThreadGroup) key).getDuration();
            attMap.put("duration", duration);

            long rampup = ((ThreadGroup) key).getRampUp();
            attMap.put("rampup", rampup);

            long threads = ((ThreadGroup) key).getNumThreads();
            attMap.put("threads", threads);

            boolean scheduler = ((ThreadGroup) key).getScheduler();
            attMap.put("scheduler", scheduler);

            long delay = ((ThreadGroup) key).getDelay();
            attMap.put("delay", delay);

            LoopController controller = (LoopController) ((ThreadGroup) key).getSamplerController();
            if (controller != null) {
                int loops = controller.getLoops();
                attMap.put("loops", loops);
                boolean isForver = controller.getPropertyAsBoolean("LoopController.continue_forever");
                attMap.put("isForver", isForver);
            }

            if (!attMap.isEmpty()) {
                jmxThreadgroup.setAttribute(JSONUtil.toJsonStr(attMap));
            }

            jmxThreadgroup.setUnit("S");
            jmxThreadgroup.setCreateTime(LocalDateTime.now());
            jmxThreadgroup.setCreateBy(script.getCreateBy());
            jmxThreadgroup.setLastUpdateBy(script.getCreateBy());
            jmxThreadgroup.setLastUpdateTime(LocalDateTime.now());
            threadgroupService.save(jmxThreadgroup);

            HashTree hashTree = item.get(key);

            initConfigElement(hashTree, script, jmxThreadgroup);

            List<JmxSample> jmxSamples = new ArrayList<>();
            initJmxSample(script, jmxThreadgroup, hashTree, jmxSamples);

            jmxThreadgroup.setLastUpdateTime(LocalDateTime.now());
            jmxThreadgroup.setAttribute(JSONUtil.toJsonStr(attMap));
            threadgroupService.updateById(jmxThreadgroup);

        }

        else if (key instanceof ConcurrencyThreadGroup) {
            JmxThreadgroup jmxThreadgroup = new JmxThreadgroup();

            Boolean flag = ((ConcurrencyThreadGroup) key).getPropertyAsBoolean(ConcurrencyThreadGroup.ENABLED);
            if (flag) {
                jmxThreadgroup.setStatus(1);
            }else{
                jmxThreadgroup.setStatus(0);
            }

            jmxThreadgroup.setJmxId(script.getId());
            String testName = ((ConcurrencyThreadGroup) key).getPropertyAsString(ConcurrencyThreadGroup.NAME);
            testName = getTestName(testName, script);
            ((ConcurrencyThreadGroup) key).setProperty(ConcurrencyThreadGroup.NAME, testName);
            jmxThreadgroup.setTestName(testName);
            String testClass = ((ConcurrencyThreadGroup) key).getPropertyAsString(ConcurrencyThreadGroup.TEST_CLASS);
            jmxThreadgroup.setTestClazz(testClass);

            String type = "";
            if (StringUtils.isNotBlank(testClass)) {
                type = testClass.substring(testClass.lastIndexOf(".")+1, testClass.length());
            }
            jmxThreadgroup.setType(type);
            Map<String, Object> attMap = new HashMap<>();
            long targetConcurrency = ((ConcurrencyThreadGroup) key).getPropertyAsLong(ConcurrencyThreadGroup.TARGET_LEVEL);
            attMap.put("targetConcurrency", targetConcurrency);

            long holdTime = ((ConcurrencyThreadGroup) key).getPropertyAsLong(ConcurrencyThreadGroup.HOLD);
            attMap.put("holdTime", holdTime);

            long rampUpTime = ((ConcurrencyThreadGroup) key).getPropertyAsLong(ConcurrencyThreadGroup.RAMP_UP);
            attMap.put("rampUpTime", rampUpTime);

            long steps = ((ConcurrencyThreadGroup) key).getPropertyAsLong(ConcurrencyThreadGroup.STEPS);
            attMap.put("steps", steps);

            String unit = ((ConcurrencyThreadGroup) key).getPropertyAsString(ConcurrencyThreadGroup.UNIT);
            attMap.put("unit", unit);

            if (!attMap.isEmpty()) {
                jmxThreadgroup.setAttribute(JSONUtil.toJsonStr(attMap));
            }

            jmxThreadgroup.setUnit(unit);
            jmxThreadgroup.setCreateTime(LocalDateTime.now());
            jmxThreadgroup.setCreateBy(script.getCreateBy());
            jmxThreadgroup.setLastUpdateBy(script.getCreateBy());
            jmxThreadgroup.setLastUpdateTime(LocalDateTime.now());
            threadgroupService.save(jmxThreadgroup);

            HashTree hashTree = item.get(key);
            initConfigElement(hashTree, script, jmxThreadgroup);

            List<JmxSample> jmxSamples = new ArrayList<>();
            initJmxSample(script, jmxThreadgroup, hashTree, jmxSamples);

            jmxThreadgroup.setLastUpdateTime(LocalDateTime.now());
            jmxThreadgroup.setAttribute(JSONUtil.toJsonStr(attMap));
            threadgroupService.updateById(jmxThreadgroup);
        }

        else  if (key instanceof ArrivalsThreadGroup) {
            JmxThreadgroup jmxThreadgroup = new JmxThreadgroup();

            Boolean flag = ((ArrivalsThreadGroup) key).getPropertyAsBoolean(ArrivalsThreadGroup.ENABLED);
            if (flag) {
                jmxThreadgroup.setStatus(1);
            }else{
                jmxThreadgroup.setStatus(0);
            }

            jmxThreadgroup.setJmxId(script.getId());
            String testName = ((ArrivalsThreadGroup) key).getPropertyAsString(ArrivalsThreadGroup.NAME);
            testName = getTestName(testName, script);
            ((ArrivalsThreadGroup) key).setProperty(ArrivalsThreadGroup.NAME, testName);
            jmxThreadgroup.setTestName(testName);
            String testClass = ((ArrivalsThreadGroup) key).getPropertyAsString(ArrivalsThreadGroup.TEST_CLASS);
            jmxThreadgroup.setTestClazz(testClass);

            String type = "";
            if (StringUtils.isNotBlank(testClass)) {
                type = testClass.substring(testClass.lastIndexOf(".")+1, testClass.length());
            }
            jmxThreadgroup.setType(type);
            Map<String, Object> attMap = new HashMap<>();
            long targetConcurrency = ((ArrivalsThreadGroup) key).getPropertyAsLong(ArrivalsThreadGroup.TARGET_LEVEL);
            attMap.put("targetConcurrency", targetConcurrency);

            long holdTime = ((ArrivalsThreadGroup) key).getPropertyAsLong(ArrivalsThreadGroup.HOLD);
            attMap.put("holdTime", holdTime);

            long rampUpTime = ((ArrivalsThreadGroup) key).getPropertyAsLong(ArrivalsThreadGroup.RAMP_UP);
            attMap.put("rampUpTime", rampUpTime);

            long steps = ((ArrivalsThreadGroup) key).getPropertyAsLong(ArrivalsThreadGroup.STEPS);
            attMap.put("steps", steps);

            String unit = ((ArrivalsThreadGroup) key).getPropertyAsString(ArrivalsThreadGroup.UNIT);
            attMap.put("unit", unit);

            if (!attMap.isEmpty()) {
                jmxThreadgroup.setAttribute(JSONUtil.toJsonStr(attMap));
            }

            jmxThreadgroup.setUnit(unit);
            jmxThreadgroup.setCreateTime(LocalDateTime.now());
            jmxThreadgroup.setCreateBy(script.getCreateBy());
            jmxThreadgroup.setLastUpdateBy(script.getCreateBy());
            jmxThreadgroup.setLastUpdateTime(LocalDateTime.now());
            threadgroupService.save(jmxThreadgroup);

            HashTree hashTree = item.get(key);
            initConfigElement(hashTree, script, jmxThreadgroup);

            List<JmxSample> jmxSamples = new ArrayList<>();
            initJmxSample(script, jmxThreadgroup, hashTree, jmxSamples);

            jmxThreadgroup.setLastUpdateTime(LocalDateTime.now());
            jmxThreadgroup.setAttribute(JSONUtil.toJsonStr(attMap));
            threadgroupService.updateById(jmxThreadgroup);
        }
    }
    private void initJmxSample(JmxScript script, JmxThreadgroup jmxThreadgroup, HashTree key, List<JmxSample> jmxSamples) {
        for (Object ky : key.keySet()) {
            initSample(key, ky, script, jmxThreadgroup, jmxSamples);

            //吞吐量控制器
            if (ky instanceof ThroughputController) {
                initThroughputSample(key, ky, script, jmxThreadgroup, jmxSamples);
            }

            //事务控制器
            else if (ky instanceof TransactionController) {
                initTransactionController(key, ky, script, jmxThreadgroup, jmxSamples);
            }


        }
        if (!jmxSamples.isEmpty()) {
            for (JmxSample jmxSample : jmxSamples) {
                sampleService.save(jmxSample);
            }
        }


    }

    private void initSample(HashTree item, Object key, JmxScript script, JmxThreadgroup jmxThreadgroup, List<JmxSample> jmxSamples) {
        if (key instanceof HTTPSamplerProxy) {
            JmxSample jmxSample = new JmxSample();
            String type = ((HTTPSamplerProxy) key).getClass().getSimpleName();
            jmxSample.setType(type);
            jmxSample.setStatus(((HTTPSamplerProxy) key).isEnabled());
            String name = ((HTTPSamplerProxy) key).getName();
            jmxSample.setName(name);
            String domain = ((HTTPSamplerProxy) key).getPropertyAsString(HTTPSamplerProxy.DOMAIN);
            jmxSample.setIp(domain);
            String port = ((HTTPSamplerProxy) key).getPropertyAsString(HTTPSamplerProxy.PORT);
            jmxSample.setPort(port);
            String encode = ((HTTPSamplerProxy) key).getPropertyAsString("HTTPSampler.contentEncoding");
            jmxSample.setEncode(encode);
            String method = ((HTTPSamplerProxy) key).getPropertyAsString("HTTPSampler.method");
            jmxSample.setMethod(method);
            String path = ((HTTPSamplerProxy) key).getPropertyAsString("HTTPSampler.path");
            jmxSample.setPath(path);
            jmxSample.setMessage("");
            jmxSample.setThreadgroupId(jmxThreadgroup.getId());
            jmxSample.setJmxId(script.getId());
            jmxSamples.add(jmxSample);
        } else if (key instanceof TCPSampler) {
            JmxSample jmxSample = new JmxSample();
            String type = ((TCPSampler) key).getClass().getSimpleName();
            jmxSample.setType(type);
            jmxSample.setStatus(((TCPSampler) key).isEnabled());
            String name = ((TCPSampler) key).getName();
            jmxSample.setName(name);
            jmxSample.setJmxId(script.getId());
            jmxSample.setThreadgroupId(jmxThreadgroup.getId());
            String ip = ((TCPSampler) key).getPropertyAsString(TCPSampler.SERVER);
            jmxSample.setIp(ip);
            String port = ((TCPSampler) key).getPropertyAsString(TCPSampler.PORT);
            jmxSample.setPort(port);
            String message = ((TCPSampler) key).getPropertyAsString(TCPSampler.REQUEST);
            jmxSample.setMessage(message);
            jmxSamples.add(jmxSample);
        }
        HashTree hashTree = item.get(key);
        initConfigElement(hashTree, script, jmxThreadgroup);
    }

    private void initTransactionController(HashTree key, Object ky, JmxScript script, JmxThreadgroup jmxThreadgroup, List<JmxSample> jmxSamples) {
    }


    private void initConfigElement(HashTree hashTree, JmxScript script, JmxThreadgroup jmxThreadgroup) {
        initHttpRequestDefault2(hashTree, script, jmxThreadgroup);
        initArguments2(hashTree, script, jmxThreadgroup);
        initHeaderManager2(hashTree, script, jmxThreadgroup);
        initResponseAssertion2(hashTree, script, jmxThreadgroup);
    }
    private void initResponseAssertion1(ResponseAssertion assertion, JmxScript jmxScript, JmxThreadgroup jmxThreadgroup) {
        String name = assertion.getPropertyAsString(ResponseAssertion.NAME);
        String assertions = assertion.getTestStrings().getStringValue();
        JSONObject attrJson = new JSONObject();
        JSONArray jsonArray = new JSONArray();
        if (StringUtils.isNotBlank(assertions)) {
            CollectionProperty testStrings = assertion.getTestStrings();
            for (int i=0;i<testStrings.size();i++) {
                jsonArray.add(testStrings.get(i).getStringValue());
            }
        }
        Boolean status = assertion.isEnabled();
        String testField = assertion.getTestField();
        Boolean assumeSuccess = assertion.getAssumeSuccess();
        Integer testType = assertion.getTestType();
        attrJson.put("testfield",testField);
        attrJson.put("assumesuccess",assumeSuccess);
        attrJson.put("testtype",testType);
        attrJson.put("teststrings", jsonArray);
        String attribute = attrJson.toString();

        JmxAssertion jmxAssertion = new JmxAssertion();
        jmxAssertion.setAttibute(attribute);
        jmxAssertion.setStatus(status);
        jmxAssertion.setJmxId(jmxScript.getId());
        if (jmxThreadgroup != null) {
            jmxAssertion.setThreadgroupId(jmxThreadgroup.getId());
        }
        jmxAssertion.setName(name);
        jmxAssertion.setType("ResponseAssertion");
        jmxAssertion.setCreateTime(LocalDateTime.now());
        jmxAssertion.setCreateBy(jmxScript.getCreateBy());
        jmxAssertion.setLastUpdateBy(jmxScript.getCreateBy());
        jmxAssertion.setLastUpdateTime(LocalDateTime.now());
        assertionService.save(jmxAssertion);

    }
    private void initResponseAssertion2(HashTree hashTree, JmxScript script, JmxThreadgroup jmxThreadgroup) {
        for (Object key : hashTree.keySet()) {
            if (key instanceof ResponseAssertion) {
                initResponseAssertion1((ResponseAssertion) key, script, jmxThreadgroup);
            }
        }
    }
    private void initThroughputSample(HashTree key, Object ky, JmxScript script, JmxThreadgroup jmxThreadgroup, List<JmxSample> jmxSamples) {
        HashTree hashTree = key.get(key);
        if (hashTree != null && hashTree.keySet().size()>0) {
            for (Object kt : hashTree.keySet()) {
                initSample(hashTree, kt, script, jmxThreadgroup, jmxSamples);
                if (kt instanceof ThroughputController) {
                    HashTree hashTree1 = hashTree.get(kt);
                    for (Object kt1 : hashTree1.keySet()) {
                        initSample(hashTree1, kt1, script, jmxThreadgroup, jmxSamples);
                        if (kt1 instanceof ThroughputController) {
                            HashTree hashTree2 = hashTree1.get(kt1);
                            for (Object kt2 : hashTree2.keySet()) {
                                initSample(hashTree2, kt2, script, jmxThreadgroup, jmxSamples);
                            }
                        }
                    }
                }
            }
        }
    }

    @Override
    public void initProperties() {
        Charset.defaultCharset();
        String jmeterHome = "";
        String jmeterBinHome = "";

//        if (System.getProperty("os.name").toLowerCase(Locale.ROOT).startsWith("win")) {
//
//        } else {
            jmeterHome = GlobalConf.getConfig("JmeterHome");
            jmeterBinHome = jmeterHome + File.separator + "bin";
            JMeterUtils.loadJMeterProperties(jmeterBinHome + File.separator + "jmeter.properties");
            log.info("jmeterBinHome:{}", jmeterBinHome);
//        }

        JMeterUtils.setJMeterHome(jmeterHome);
        JMeterUtils.initLocale();
        Properties jmeterProps = JMeterUtils.getJMeterProperties();
        String userProp = JMeterUtils.getPropDefault("user.properties", "");
        if (userProp.length() > 0) { //$NON-NLS-1$
            File file = JMeterUtils.findFile(userProp);
            if (file.canRead()) {
                try (FileInputStream fis = new FileInputStream(file)) {
                    log.info("Loading user properties from: {}", file);
                    Properties tmp = new Properties();
                    tmp.load(fis);
                    jmeterProps.putAll(tmp);
                } catch (IOException e) {
                    log.warn("Error loading user property file: {}", userProp, e);
                }
            }
        }

        // Add local system properties, if the file is found
        String sysProp = JMeterUtils.getPropDefault("system.properties", ""); //$NON-NLS-1$
        if (sysProp.length() > 0) {
            File file = JMeterUtils.findFile(sysProp);
            if (file.canRead()) {
                try (FileInputStream fis = new FileInputStream(file)) {
                    log.info("Loading system properties from: {}", file);
                    System.getProperties().load(fis);
                } catch (IOException e) {
                    log.warn("Error loading system property file: {}", sysProp, e);
                }
            }
        }
        jmeterProps.put("jmeter.version", JMeterUtils.getJMeterVersion());
    }


    private String getName(String name, JmxScript script) {
        QueryWrapper<JmxArguments> wrapper = new QueryWrapper<>();
        wrapper.eq("name", name);
        wrapper.eq("jmx_id", script.getId());

        List<JmxArguments> list = argumentsService.list(wrapper);
        if(list.isEmpty()){
            return name;
        }else{
            LambdaQueryWrapper<JmxArguments> w = Wrappers.<JmxArguments>lambdaQuery().likeRight(JmxArguments::getName, name).eq(JmxArguments::getJmxId, script.getId());
            return name + "_" + argumentsService.list(w);
        }
    }

    private String getTestName(String testName, JmxScript script) {
        QueryWrapper<JmxThreadgroup> wrapper = new QueryWrapper<>();
        wrapper.eq("test_name", testName);
        wrapper.eq("jmx_id", script.getId());

        List<JmxThreadgroup> list = threadgroupService.list(wrapper);
        if(list.isEmpty()){
            return testName;
        }else{
            return testName + "_" + list.size();
        }
    }

    @Override
    public List<JmxArguments> getArguments(int id) {
        LambdaQueryWrapper<JmxArguments> w = Wrappers.<JmxArguments>lambdaQuery().eq(JmxArguments::getJmxId, id);
        List<JmxArguments> argumentsList = argumentsService.list(w);
        for (JmxArguments arguments: argumentsList) {
            JSONArray jsonArray = JSONUtil.parseArray(arguments.getArguments());
            arguments.setArgumentsArray(jsonArray);

            Integer threadgroupId = arguments.getThreadgroupId();
            if (threadgroupId == 0) {
                arguments.setThreadGroupName("/");
            } else {
                JmxThreadgroup threadgroup = threadgroupService.getById(threadgroupId);
                arguments.setThreadGroupName(threadgroup.getTestName());
            }
        }

        return argumentsList;
    }

    @Override
    public void updateArgument(JmxArguments arguments) throws Exception {
        Integer jmxId = arguments.getJmxId();
        JmxScript jmxScript = getById(jmxId);
        File newFile = readFSScriptFileToLocal(jmxScript);

        initProperties();

        HashTree jmxTree = SaveService.loadTree(newFile);
        Set keys = jmxTree.keySet();

        for (Object key : keys) {
            updateAbstractArguments(key, jmxTree, arguments, jmxTree, jmxScript.getPath(), newFile);
        }
        for (HashTree item : jmxTree.values()) {
            Set treeKeys = item.keySet();
            for (Object key : treeKeys) {
                if(key instanceof Arguments){
                    updateArguments((Arguments) key, arguments, jmxTree, jmxScript.getPath(), newFile);
                }
                updateAbstractArguments(key, item, arguments, jmxTree, jmxScript.getPath(), newFile);
            }
        }
    }

    private void updateAbstractArguments(Object key, HashTree item, JmxArguments arguments, HashTree jmxTree, String path, File jmxFile) throws Exception {
        HashTree hashTree = item.get(key);
        updateConfigElement(hashTree, arguments, jmxTree, path, jmxFile);
        updateJmxSample(hashTree, arguments, jmxTree, path, jmxFile);
    }

    private void updateJmxSample(HashTree hashTree, JmxArguments arguments, HashTree jmxTree, String path, File jmxFile) throws Exception{
        for (Object key : hashTree.keySet()) {
            updateSmaple(hashTree, key, arguments, jmxTree, path, jmxFile);

            //吞吐量控制器
            if (key instanceof ThroughputController) {
                updateThroughputSmaple(hashTree, key, arguments, jmxTree, path, jmxFile);
            } else if (key instanceof TransactionController) {
                //事务控制器
                updateTransactionSmaple(hashTree, key, arguments, jmxTree, path, jmxFile);
            }
        }
    }

    private void updateTransactionSmaple(HashTree hashTree, Object key, JmxArguments arguments, HashTree jmxTree, String path, File jmxFile) throws Exception {
        HashTree tree = hashTree.get(key);
        if (tree == null || tree.keySet().size() == 0) {
            return;
        }
        for (Object tk : tree.keySet()) {
            if (tk instanceof Arguments) {
                updateArguments((Arguments) tk, arguments, jmxTree, path, jmxFile);
            }

            if (tk instanceof HTTPSamplerProxy || tk instanceof TCPSampler) {
                updateSmaple(tree, tk, arguments, jmxTree, path, jmxFile);
            }

            if (tk instanceof TransactionController) {
                HashTree hashTree1 = tree.get(tk);
                for (Object kt1 : hashTree1.keySet()) {
                    if (kt1 instanceof Arguments) {
                        updateArguments((Arguments) kt1, arguments, jmxTree, path, jmxFile);
                    }  else if (kt1 instanceof HTTPSamplerProxy || kt1 instanceof TCPSampler) {
                        updateSmaple(hashTree1, kt1, arguments, jmxTree, path, jmxFile);
                    }else if (kt1 instanceof TransactionController) {
                        HashTree hashTree2 = hashTree1.get(kt1);
                        for (Object kt2 : hashTree2.keySet()) {
                            if (kt2 instanceof Arguments) {
                                updateArguments((Arguments) kt2, arguments, jmxTree, path, jmxFile);
                            }else if (kt2 instanceof HTTPSamplerProxy || kt2 instanceof TCPSampler) {
                                updateSmaple(hashTree2, kt2, arguments, jmxTree, path, jmxFile);
                            }
                        }
                    }
                }
            }
        }
    }

    private void updateThroughputSmaple(HashTree hashTree, Object key, JmxArguments arguments, HashTree jmxTree, String path, File jmxFile)  throws Exception{
        HashTree tree = hashTree.get(key);
        if (tree == null || tree.keySet().size() == 0) {
            return;
        }
        for (Object tk : tree.keySet()) {
            updateSmaple(hashTree, tk, arguments, jmxTree, path, jmxFile);
            if (tk instanceof ThroughputController) {
                HashTree hashTree1 = hashTree.get(tk);
                for (Object tk1 : hashTree1.keySet()) {
                    updateSmaple(hashTree1, tk1, arguments, jmxTree, path, jmxFile);
                    if (tk1 instanceof ThroughputController) {
                        HashTree hashTree2 = hashTree1.get(tk1);
                        for (Object tk2 : hashTree2.keySet()) {
                            updateSmaple(hashTree2, tk2, arguments, jmxTree, path, jmxFile);
                        }
                    }
                }
            }
        }
    }

    private void updateSmaple(HashTree hashTree, Object key, JmxArguments arguments, HashTree jmxTree, String path, File jmxFile) throws Exception{
        HashTree tree = hashTree.get(key);
        updateConfigElement(tree, arguments, jmxTree, path, jmxFile);
    }

    private void updateConfigElement(HashTree hashTree, JmxArguments arguments, HashTree jmxTree, String path, File jmxFile) throws Exception {
        Set keys = hashTree.keySet();
        for (Object key : keys) {
            if (key instanceof Arguments) {
                updateArguments((Arguments)key, arguments, jmxTree, path, jmxFile);
            }
        }
    }

    private void updateArguments(Arguments key, JmxArguments arguments, HashTree jmxTree, String path, File jmxFile) throws Exception {
        JmxArguments oriArguments = argumentsService.getById(arguments.getId());
        if (oriArguments != null && oriArguments.getName() != null && oriArguments.getName().equals(key.getName())) {
            JSONArray array = new JSONArray();
            for (int i = 0; i < key.getArgumentCount(); i++) {
                JSONObject subJson = (JSONObject) arguments.getArgumentsArray().get(i);
                array.add(subJson);

                key.getArgument(i).setName((String) subJson.get("name"));
                key.getArgument(i).setValue((String) subJson.get("value"));
                key.getArgument(i).setDescription((String) subJson.get("desc"));
            }

            oriArguments.setArguments(array.toString());
            oriArguments.setLastUpdateBy(SessionUtil.getCurrentUser());
            oriArguments.setLastUpdateTime(LocalDateTime.now());
            argumentsService.updateById(oriArguments);

            //更新本地文件
            SaveService.saveTree(jmxTree, new FileOutputStream(jmxFile));

            //更新远程文件
            overrideRemoteFile(arguments.getJmxId(), path.substring(path.lastIndexOf("/")), FileUtil.getInputStream(jmxFile));
        }
    }

    @Override
    public File readFSScriptFileToLocal(JmxScript script) throws IOException {
        String remoteFileFullName = script.getPath();

        String localFileDir = JMX_LOCAL_DIR + "/" + script.getId();
        if (!new File(localFileDir).exists()) {
            FileUtil.mkdir(localFileDir);
            log.info("mkdir.localFileDir");
        }

        String localFileFullName = localFileDir + "/" +  remoteFileFullName.substring(remoteFileFullName.lastIndexOf("/"));
        File localFile = new File(localFileFullName);
        if (localFile.exists()) {
            localFile.delete();
            log.info("delete old file");
        }

        File newFile = FileUtil.touch(new File(localFileFullName));
        FileUtil.writeFromStream(FileUtil.getInputStream(new File(remoteFileFullName)), newFile, true);
        return newFile;
    }

    /**
     * 覆盖远程脚本
     * @param jmxId
     * @param fileName
     * @param inputStream
     */
    @Override
    public void overrideRemoteFile(Integer jmxId, String fileName, InputStream inputStream) {
        String storeDir = Constant.JMX_FILE_SERVER_DIR + "/" + jmxId;
        File storeDirFile = new File(storeDir);
        if (!storeDirFile.exists()) {
            FileUtil.mkdir(storeDir);
            log.info("mkdir.storeDir");
        }

        String scriptFileDir = storeDir + "/" + fileName;
        File scriptFile = new File(scriptFileDir);
        if (scriptFile.exists()) {
            scriptFile.delete();
            log.info("delete old file");
        }

        File newFile = FileUtil.touch(new File(scriptFileDir));
        FileUtil.writeFromStream(inputStream, newFile, true);
    }

}
